// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.Collections;
using System.IO;
using System.IO.Compression;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Text;
using System.Drawing;
using CIP4.Common;
using CIP4.Item;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;

namespace CIP4.Serializer
{
    public class SerializerBaseType
    {
        /// <summary>
        /// Is set by the WrapperSerializerType when the Serializer is fetched from the defining assembly
        /// </summary>
        public ISerializerType SerializerWrapper { get; set; }

        public delegate System.Collections.IDictionary GetDictionaryDelegate();

        public delegate object GetKeyDelegate(string keyString);

        #region string variants of the <type>Value functions

// ReSharper disable UnusedMember.Global
        // The warings are useless here - these functions are used by the serializers which derive from SerializerBaseType
// ReSharper disable InconsistentNaming
        // the names are non-standard because they have to mactch the CLR type names

        protected static int IntValue(object objectValue, int defaultValue)
        {
            if (objectValue is int)
                return (int)objectValue;

            if (objectValue == null)
                return 0;

            string stringValue = objectValue.ToString();
            int value;
            if (string.IsNullOrEmpty(stringValue))
                if (int.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static int? IntValueobject(object objectValue, int? defaultValue)
        {
            if (objectValue == null)
                return default(int?);
            if (objectValue is int?)
                return (int?)objectValue;
            string stringValue = objectValue.ToString();
            int value;
            if (string.IsNullOrEmpty(stringValue))
                if (int.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static string StringValue(object objectValue, string defaultValue)
        {
            if (objectValue == null)
                return default(string);
            if (objectValue is string)
                return (string)objectValue;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue))
                return stringValue;
            return defaultValue;
        }
        protected static Int32 Int32Value(object objectValue, Int32 defaultValue)
        {
            if (objectValue == null)
                return default(Int32);
            if (objectValue is Int32)
                return (Int32)objectValue;
            string stringValue = objectValue.ToString();
            Int32 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Int32.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Int32? Int32Valueobject(object objectValue, Int32? defaultValue)
        {
            if (objectValue == null)
                return default(Int32);
            if (objectValue is Int32)
                return (Int32)objectValue;
            string stringValue = objectValue.ToString();
            Int32 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Int32.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static bool boolValue(object objectValue, bool defaultValue)
        {
            if (objectValue == null)
                return default(bool);
            if (objectValue is bool)
                return (bool)objectValue;
            string stringValue = objectValue.ToString();
            bool value;
            if (!string.IsNullOrEmpty(stringValue))
                if (bool.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static bool? boolValueobject(object objectValue, bool? defaultValue)
        {
            if (objectValue == null)
                return default(bool);
            if (objectValue is bool)
                return (bool)objectValue;
            string stringValue = objectValue.ToString();
            bool value;
            if (!string.IsNullOrEmpty(stringValue))
                if (bool.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Boolean BooleanValue(object objectValue, Boolean defaultValue)
        {
            if (objectValue == null)
                return default(Boolean);
            if (objectValue is Boolean)
                return (Boolean)objectValue;
            string stringValue = objectValue.ToString();
            Boolean value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Boolean.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Boolean? BooleanValue(object objectValue, Boolean? defaultValue)
        {
            if (objectValue == null)
                return default(Boolean?);
            if (objectValue is Boolean?)
                return (Boolean?)objectValue;
            string stringValue = objectValue.ToString();
            Boolean value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Boolean.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static byte byteValue(object objectValue, byte defaultValue)
        {
            if (objectValue == null)
                return default(byte);
            if (objectValue is byte)
                return (byte)objectValue;
            string stringValue = objectValue.ToString();
            byte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (byte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static byte? byteValue(object objectValue, byte? defaultValue)
        {
            if (objectValue == null)
                return default(byte?);
            if (objectValue is byte?)
                return (byte?)objectValue;
            string stringValue = objectValue.ToString();
            byte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (byte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static sbyte sbyteValue(object objectValue, sbyte defaultValue)
        {
            if (objectValue == null)
                return default(sbyte);
            if (objectValue is sbyte)
                return (sbyte)objectValue;
            string stringValue = objectValue.ToString();
            sbyte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (sbyte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static sbyte? sbyteValue(object objectValue, sbyte? defaultValue)
        {
            if (objectValue == null)
                return default(sbyte?);
            if (objectValue is sbyte?)
                return (sbyte?)objectValue;
            string stringValue = objectValue.ToString();
            sbyte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (sbyte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        //protected static char charValue(string stringValue) 
        //{
        //    if (!string.IsNullOrEmpty(stringValue))
        //        return stringValue[0];
        //    return '\0';
        //}
        protected static decimal decimalValue(object objectValue, decimal defaultValue)
        {
            if (objectValue == null)
                return default(decimal);
            if (objectValue is decimal)
                return (decimal)objectValue;
            string stringValue = objectValue.ToString();
            decimal value;
            if (!string.IsNullOrEmpty(stringValue))
                if (decimal.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static decimal? decimalValue(object objectValue, decimal? defaultValue)
        {
            if (objectValue == null)
                return default(decimal?);
            if (objectValue is decimal?)
                return (decimal?)objectValue;
            string stringValue = objectValue.ToString();
            decimal value;
            if (!string.IsNullOrEmpty(stringValue))
                if (decimal.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static double doubleValue(object objectValue, double defaultValue)
        {
            if (objectValue == null)
                return default(double);
            if (objectValue is double)
                return (double)objectValue;
            string stringValue = objectValue.ToString();
            double value;
            if (!string.IsNullOrEmpty(stringValue))
                if (double.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static double? doubleValue(object objectValue, double? defaultValue)
        {
            if (objectValue == null)
                return default(double?);
            if (objectValue is double?)
                return (double?)objectValue;
            string stringValue = objectValue.ToString();
            double value;
            if (!string.IsNullOrEmpty(stringValue))
                if (double.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static float floatValue(object objectValue, float defaultValue)
        {
            if (objectValue == null)
                return default(float);
            if (objectValue is float)
                return (float)objectValue;
            string stringValue = objectValue.ToString();
            float value;
            if (!string.IsNullOrEmpty(stringValue))
                if (float.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static float? floatValue(object objectValue, float? defaultValue)
        {
            if (objectValue == null)
                return default(float?);
            if (objectValue is float?)
                return (float?)objectValue;
            string stringValue = objectValue.ToString();
            float value;
            if (!string.IsNullOrEmpty(stringValue))
                if (float.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static uint uintValue(object objectValue, uint defaultValue)
        {
            if (objectValue == null)
                return default(uint);
            if (objectValue is uint)
                return (uint)objectValue;
            string stringValue = objectValue.ToString();
            uint value;
            if (!string.IsNullOrEmpty(stringValue))
                if (uint.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static uint? uintValue(object objectValue, uint? defaultValue)
        {
            if (objectValue == null)
                return default(uint?);
            if (objectValue is uint?)
                return (uint?)objectValue;
            string stringValue = objectValue.ToString();
            uint value;
            if (!string.IsNullOrEmpty(stringValue))
                if (uint.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static long longValue(object objectValue, long defaultValue)
        {
            if (objectValue == null)
                return default(long);
            if (objectValue is long)
                return (long)objectValue;
            string stringValue = objectValue.ToString();
            long value;
            if (!string.IsNullOrEmpty(stringValue))
                if (long.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static long? longValue(object objectValue, long? defaultValue)
        {
            if (objectValue == null)
                return default(long?);
            if (objectValue is long?)
                return (long?)objectValue;
            string stringValue = objectValue.ToString();
            long value;
            if (!string.IsNullOrEmpty(stringValue))
                if (long.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static ulong ulongValue(object objectValue, ulong defaultValue)
        {
            if (objectValue == null)
                return default(ulong);
            if (objectValue is ulong)
                return (ulong)objectValue;
            string stringValue = objectValue.ToString();
            ulong value;
            if (!string.IsNullOrEmpty(stringValue))
                if (ulong.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static ulong? ulongValue(object objectValue, ulong? defaultValue)
        {
            if (objectValue == null)
                return default(ulong?);
            if (objectValue is ulong?)
                return (ulong?)objectValue;
            string stringValue = objectValue.ToString();
            ulong value;
            if (!string.IsNullOrEmpty(stringValue))
                if (ulong.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static short shortValue(object objectValue, short defaultValue)
        {
            if (objectValue == null)
                return default(short);
            if (objectValue is short)
                return (short)objectValue;
            string stringValue = objectValue.ToString();
            short value;
            if (!string.IsNullOrEmpty(stringValue))
                if (short.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static short? shortValue(object objectValue, short? defaultValue)
        {
            if (objectValue == null)
                return default(short?);
            if (objectValue is short?)
                return (short?)objectValue;
            string stringValue = objectValue.ToString();
            short value;
            if (!string.IsNullOrEmpty(stringValue))
                if (short.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static ushort ushortValue(object objectValue, ushort defaultValue)
        {
            if (objectValue == null)
                return default(ushort);
            if (objectValue is ushort)
                return (ushort)objectValue;
            string stringValue = objectValue.ToString();
            ushort value;
            if (!string.IsNullOrEmpty(stringValue))
                if (ushort.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static ushort? ushortValue(object objectValue, ushort? defaultValue)
        {
            if (objectValue == null)
                return default(ushort?);
            if (objectValue is ushort?)
                return (ushort?)objectValue;
            string stringValue = objectValue.ToString();
            ushort value;
            if (!string.IsNullOrEmpty(stringValue))
                if (ushort.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Byte ByteValue(object objectValue, Byte defaultValue)
        {
            if (objectValue == null)
                return default(Byte);
            if (objectValue is Byte)
                return (Byte)objectValue;
            string stringValue = objectValue.ToString();
            Byte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Byte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Byte? ByteValue(object objectValue, Byte? defaultValue)
        {
            if (objectValue == null)
                return default(Byte?);
            if (objectValue is Byte?)
                return (Byte?)objectValue;
            string stringValue = objectValue.ToString();
            Byte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Byte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Char CharValue(object objectValue, Char defaultValue)
        {
            if (objectValue == null)
                return default(Char);
            if (objectValue is Char)
                return (Char)objectValue;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue) && stringValue.Length > 0)
            {
                return WrapperSerializerType.ConvertToChar(stringValue);
            }
            return defaultValue;
        }
        protected static Char? CharValue(object objectValue, Char? defaultValue)
        {
            if (objectValue == null)
                return default(Char?);
            if (objectValue is Char?)
                return (Char?)objectValue;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue) && stringValue.Length > 0)
            {
                return WrapperSerializerType.ConvertToChar(stringValue);
            }
            return defaultValue;
        }
        protected static DateTime DateTimeValue(object objectValue, DateTime defaultValue)
        {
            if (objectValue == null)
                return default(DateTime);
            if (objectValue is DateTime)
                return (DateTime)objectValue;
            string stringValue = objectValue.ToString();

            DateTime value;
            if (!string.IsNullOrEmpty(stringValue))
                if (DateTime.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static DateTime? DateTimeValue(object objectValue, DateTime? defaultValue)
        {
            if (objectValue == null)
                return default(DateTime?);
            if (objectValue is DateTime?)
                return (DateTime?)objectValue;
            string stringValue = objectValue.ToString();
            DateTime value;
            if (!string.IsNullOrEmpty(stringValue))
                if (DateTime.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static DateTimeOffset DateTimeOffsetValue(object objectValue, DateTimeOffset defaultValue)
        {
            if (objectValue == null)
                return default(DateTimeOffset);
            if (objectValue is DateTimeOffset)
                return (DateTimeOffset)objectValue;
            string stringValue = objectValue.ToString();
            DateTimeOffset value;
            if (!string.IsNullOrEmpty(stringValue))
                if (DateTimeOffset.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static DateTimeOffset? DateTimeOffsetValue(object objectValue, DateTimeOffset? defaultValue)
        {
            if (objectValue == null)
                return default(DateTimeOffset?);
            if (objectValue is DateTimeOffset?)
                return (DateTimeOffset?)objectValue;
            string stringValue = objectValue.ToString();
            DateTimeOffset value;
            if (!string.IsNullOrEmpty(stringValue))
                if (DateTimeOffset.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Decimal DecimalValue(object objectValue, Decimal defaultValue)
        {
            if (objectValue == null)
                return default(Decimal);
            if (objectValue is Decimal)
                return (Decimal)objectValue;
            string stringValue = objectValue.ToString();
            Decimal value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Decimal.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Decimal? DecimalValue(object objectValue, Decimal? defaultValue)
        {
            if (objectValue == null)
                return default(Decimal?);
            if (objectValue is Decimal?)
                return (Decimal?)objectValue;
            string stringValue = objectValue.ToString();
            Decimal value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Decimal.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Double DoubleValue(object objectValue, Double defaultValue)
        {
            if (objectValue == null)
                return default(Double);
            if (objectValue is Double)
                return (Double)objectValue;
            string stringValue = objectValue.ToString();
            Double value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Double.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Double? DoubleValue(object objectValue, Double? defaultValue)
        {
            if (objectValue == null)
                return default(Double?);
            if (objectValue is Double?)
                return (Double?)objectValue;
            string stringValue = objectValue.ToString();
            Double value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Double.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Guid GuidValue(object objectValue, Guid defaultValue)
        {
            if (objectValue == null)
                return default(Guid);
            if (objectValue is Guid)
                return (Guid)objectValue;
            if (objectValue is BsonValue)
                return ((BsonValue) objectValue).AsGuid;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue))
                try
                {
                    return new Guid(stringValue);
                }
                catch (Exception)
                {
                    return default(Guid);
                }
            return defaultValue;
        }
        protected static Guid? GuidValue(object objectValue, Guid? defaultValue)
        {
            if (objectValue == null)
                return default(Guid?);
            if (objectValue is Guid?)
                return (Guid?)objectValue;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue))
                try
                {
                    return new Guid(stringValue);
                }
                catch (Exception)
                {
                    return default(Guid);
                }
            return defaultValue;
        }
        protected static Int16 Int16Value(object objectValue, Int16 defaultValue)
        {
            if (objectValue == null)
                return default(Int16);
            if (objectValue is Int16)
                return (Int16)objectValue;
            string stringValue = objectValue.ToString();
            Int16 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Int16.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Int16? Int16Value(object objectValue, Int16? defaultValue)
        {
            if (objectValue == null)
                return default(Int16?);
            if (objectValue is Int16?)
                return (Int16?)objectValue;
            string stringValue = objectValue.ToString();
            Int16 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Int16.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Int64 Int64Value(object objectValue, Int64 defaultValue)
        {
            if (objectValue == null)
                return default(Int64);
            if (objectValue is Int64)
                return (Int64)objectValue;
            string stringValue = objectValue.ToString();
            Int64 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Int64.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Int64? Int64Value(object objectValue, Int64? defaultValue)
        {
            if (objectValue == null)
                return default(Int64?);
            if (objectValue is Int64?)
                return (Int64?)objectValue;
            string stringValue = objectValue.ToString();
            Int64 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Int64.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static SByte SByteValue(object objectValue, SByte defaultValue)
        {
            if (objectValue == null)
                return default(SByte);
            if (objectValue is SByte)
                return (SByte)objectValue;
            string stringValue = objectValue.ToString();
            SByte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (SByte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static SByte? SByteValue(object objectValue, SByte? defaultValue)
        {
            if (objectValue == null)
                return default(SByte?);
            if (objectValue is SByte?)
                return (SByte?)objectValue;
            string stringValue = objectValue.ToString();
            SByte value;
            if (!string.IsNullOrEmpty(stringValue))
                if (SByte.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Single SingleValue(object objectValue, Single defaultValue)
        {
            if (objectValue == null)
                return default(Single);
            if (objectValue is Single)
                return (Single)objectValue;
            string stringValue = objectValue.ToString();
            Single value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Single.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static Single? SingleValue(object objectValue, Single? defaultValue)
        {
            if (objectValue == null)
                return default(Single?);
            if (objectValue is Single?)
                return (Single?)objectValue;
            string stringValue = objectValue.ToString();
            Single value;
            if (!string.IsNullOrEmpty(stringValue))
                if (Single.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static TimeSpan TimeSpanValue(object objectValue, TimeSpan defaultValue)
        {
            if (objectValue == null)
                return default(TimeSpan);
            if (objectValue is TimeSpan)
                return (TimeSpan)objectValue;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue))
            {
                if (stringValue.StartsWith("PT"))
                {
                    try
                    {
                        return XmlConvert.ToTimeSpan(stringValue);
                    }
                    catch (Exception)
                    {
                        Globals.DefaultLogger(MajorEnum.Error, MinorEnum.SerializationError, "Given value is not a valid timespan (should conform to IS) 8601: {0}", new object[] { stringValue });
                    }
                }
                else
                {
                    return TimeSpan.Parse(stringValue);
                }
            }
            return defaultValue;
        }
        protected static TimeSpan? TimeSpanValue(object objectValue, TimeSpan? defaultValue)
        {
            if (objectValue == null)
                return default(TimeSpan?);
            if (objectValue is TimeSpan?)
                return (TimeSpan?)objectValue;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue))
                try
                {
                    return XmlConvert.ToTimeSpan(stringValue);
                }
                catch (Exception)
                {
                    Globals.DefaultLogger(MajorEnum.Error, MinorEnum.SerializationError, "Given value is not a valid timespan (should conform to IS) 8601: {0}", new object[] { stringValue });
                }
            return defaultValue;
        }
        protected static TimeZoneInfo TimeZoneInfoValue(object objectValue, TimeZoneInfo defaultValue)
        {
            if (objectValue == null)
                return default(TimeZoneInfo);
            if (objectValue is TimeZoneInfo)
                return (TimeZoneInfo)objectValue;
            string stringValue = objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue))
                return TimeZoneInfo.FromSerializedString(stringValue);
            return defaultValue;
        }
        protected static UInt16 UInt16Value(object objectValue, UInt16 defaultValue)
        {
            if (objectValue == null)
                return default(UInt16);
            if (objectValue is UInt16)
                return (UInt16)objectValue;
            string stringValue = objectValue.ToString();
            UInt16 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (UInt16.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt16? UInt16Value(object objectValue, UInt16? defaultValue)
        {
            if (objectValue == null)
                return default(UInt16?);
            if (objectValue is UInt16?)
                return (UInt16?)objectValue;
            string stringValue = objectValue.ToString();
            UInt16 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (UInt16.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt32 UInt32Value(object objectValue, UInt32 defaultValue)
        {
            if (objectValue == null)
                return default(UInt32);
            if (objectValue is UInt32)
                return (UInt32)objectValue;
            string stringValue = objectValue.ToString();
            UInt32 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (UInt32.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt32? UInt32Value(object objectValue, UInt32? defaultValue)
        {
            if (objectValue == null)
                return default(UInt32?);
            if (objectValue is UInt32?)
                return (UInt32?)objectValue;
            string stringValue = objectValue.ToString();
            UInt32 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (UInt32.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt64 UInt64Value(object objectValue, UInt64 defaultValue)
        {
            if (objectValue == null)
                return default(UInt64);
            if (objectValue is UInt64)
                return (UInt64)objectValue;
            string stringValue = objectValue.ToString();
            UInt64 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (UInt64.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt64? UInt64Value(object objectValue, UInt64? defaultValue)
        {
            if (objectValue == null)
                return default(UInt64?);
            if (objectValue is UInt64?)
                return (UInt64?)objectValue;
            string stringValue = objectValue.ToString();
            UInt64 value;
            if (!string.IsNullOrEmpty(stringValue))
                if (UInt64.TryParse(stringValue, out value))
                    return value;
            return defaultValue;
        }
        // ReSharper disable PossibleNullReferenceException
        protected static Color ColorValue(object objectValue, Color defaultValue)
        {
            if (objectValue == null)
                return default(Color);
            if (objectValue is Color)
                return (Color)objectValue;
            string stringValue = objectValue.ToString();
            ColorConverter cc = new ColorConverter();
            if (!string.IsNullOrEmpty(stringValue))
            {
                if (stringValue.StartsWith("Color"))
                {
                    // for example Color [A=0, R=1, G=134, B=159]
                    string[] parts = stringValue.Split(new[] { '[', ']', '=', ',' }, StringSplitOptions.RemoveEmptyEntries);
                    return Color.FromArgb(int.Parse(parts[2]), int.Parse(parts[4]), int.Parse(parts[6]), int.Parse(parts[8]));
                }
                return (Color)cc.ConvertFromString(stringValue);
            }
            return defaultValue;
        }
        protected static Color? ColorValue(object objectValue, Color? defaultValue)
        {
            if (objectValue == null)
                return default(Color?);
            if (objectValue is Color?)
                return (Color?)objectValue;
            string stringValue = objectValue.ToString();
            ColorConverter cc = new ColorConverter();
            if (!string.IsNullOrEmpty(stringValue))
                return (Color)cc.ConvertFromString(stringValue);
            return defaultValue;
        }
        // ReSharper restore PossibleNullReferenceException
        protected static string RgbForColor(Color value, string defaultValue)
        {
            ColorConverter cc = new ColorConverter();
            if (value != default(Color))
                return cc.ConvertToString(value);
            return defaultValue;
        }
        protected static string RgbForColor(Color value)
        {
            ColorConverter cc = new ColorConverter();
            return cc.ConvertToString(value);
        }
        protected static Uri UriValue(object objectValue, Uri defaultValue)
        {
            if (objectValue == null)
                return defaultValue;
            if (objectValue is Uri)
                return (Uri) objectValue;
            string stringValue = 
                objectValue is XElement? ((XElement)objectValue).Value:
                objectValue is BsonValue ? ((BsonValue)objectValue).AsString:
                objectValue.ToString();
            if (!string.IsNullOrEmpty(stringValue))
                return new Uri(stringValue);
            return defaultValue;

        }

        #endregion

        #region Xelement variants of the <type>Value functions

        protected static int IntValue(XElement element, int defaultValue)
        {
            int value;
            if (element != null)
                if (int.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static int? IntValue(XElement element, int? defaultValue)
        {
            int value;
            if (element != null)
                if (int.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static string StringValue(XElement element, string defaultValue)
        {
            if (element != null)
                return element.Value;
            return defaultValue;
        }
        protected static Int32 Int32Value(XElement element, Int32 defaultValue)
        {
            Int32 value;
            if (element != null)
                if (Int32.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Int32? Int32Value(XElement element, Int32? defaultValue)
        {
            Int32 value;
            if (element != null)
                if (Int32.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static bool boolValue(XElement element, bool defaultValue)
        {
            bool value;
            if (element != null)
                if (bool.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static bool? boolValue(XElement element, bool? defaultValue)
        {
            bool value;
            if (element != null)
                if (bool.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Boolean BooleanValue(XElement element, Boolean defaultValue)
        {
            Boolean value;
            if (element != null)
                if (Boolean.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Boolean? BooleanValue(XElement element, Boolean? defaultValue)
        {
            Boolean value;
            if (element != null)
                if (Boolean.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static byte byteValue(XElement element, byte defaultValue)
        {
            byte value;
            if (element != null)
                if (byte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static byte? byteValue(XElement element, byte? defaultValue)
        {
            byte value;
            if (element != null)
                if (byte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static sbyte sbyteValue(XElement element, sbyte defaultValue)
        {
            sbyte value;
            if (element != null)
                if (sbyte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static sbyte? sbyteValue(XElement element, sbyte? defaultValue)
        {
            sbyte value;
            if (element != null)
                if (sbyte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        //protected static char charValue(XElement element) 
        //{
        //    if (element != null)
        //        return element.Value[0];
        //    return '\0';
        //}
        protected static decimal decimalValue(XElement element, decimal defaultValue)
        {
            decimal value;
            if (element != null)
                if (decimal.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static decimal? decimalValue(XElement element, decimal? defaultValue)
        {
            decimal value;
            if (element != null)
                if (decimal.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static double doubleValue(XElement element, double defaultValue)
        {
            double value;
            if (element != null)
                if (double.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static double? doubleValue(XElement element, double? defaultValue)
        {
            double value;
            if (element != null)
                if (double.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static float floatValue(XElement element, float defaultValue)
        {
            float value;
            if (element != null)
                if (float.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static float? floatValue(XElement element, float? defaultValue)
        {
            float value;
            if (element != null)
                if (float.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static int intValue(XElement element, int defaultValue)
        {
            int value;
            if (element != null)
                if (int.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static int? intValue(XElement element, int? defaultValue)
        {
            int value;
            if (element != null)
                if (int.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static uint uintValue(XElement element, uint defaultValue)
        {
            uint value;
            if (element != null)
                if (uint.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static uint? uintValue(XElement element, uint? defaultValue)
        {
            uint value;
            if (element != null)
                if (uint.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static long longValue(XElement element, long defaultValue)
        {
            long value;
            if (element != null)
                if (long.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static long? longValue(XElement element, long? defaultValue)
        {
            long value;
            if (element != null)
                if (long.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static ulong ulongValue(XElement element, ulong defaultValue)
        {
            ulong value;
            if (element != null)
                if (ulong.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static ulong? ulongValue(XElement element, ulong? defaultValue)
        {
            ulong value;
            if (element != null)
                if (ulong.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static short shortValue(XElement element, short defaultValue)
        {
            short value;
            if (element != null)
                if (short.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static short? shortValue(XElement element, short? defaultValue)
        {
            short value;
            if (element != null)
                if (short.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static ushort ushortValue(XElement element, ushort defaultValue)
        {
            ushort value;
            if (element != null)
                if (ushort.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static ushort? ushortValue(XElement element, ushort? defaultValue)
        {
            ushort value;
            if (element != null)
                if (ushort.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Byte ByteValue(XElement element, Byte defaultValue)
        {
            Byte value;
            if (element != null)
                if (Byte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Byte? ByteValue(XElement element, Byte? defaultValue)
        {
            Byte value;
            if (element != null)
                if (Byte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Char CharValue(XElement element, Char defaultValue)
        {
            if (element != null && element.Value.Length > 0)
            {
                return WrapperSerializerType.ConvertToChar(element.Value);
            }
            return defaultValue;
        }
        protected static Char? CharValue(XElement element, Char? defaultValue)
        {
            if (element != null && element.Value.Length > 0)
            {
                return WrapperSerializerType.ConvertToChar(element.Value);
            }
            return defaultValue;
        }
        protected static DateTime DateTimeValue(XElement element, DateTime defaultValue)
        {
            DateTime value;
            if (element != null)
                if (DateTime.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static DateTime? DateTimeValue(XElement element, DateTime? defaultValue)
        {
            DateTime value;
            if (element != null)
                if (DateTime.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static DateTimeOffset DateTimeOffsetValue(XElement element, DateTimeOffset defaultValue)
        {
            DateTimeOffset value;
            if (element != null)
                if (DateTimeOffset.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static DateTimeOffset? DateTimeOffsetValue(XElement element, DateTimeOffset? defaultValue)
        {
            DateTimeOffset value;
            if (element != null)
                if (DateTimeOffset.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Decimal DecimalValue(XElement element, Decimal defaultValue)
        {
            Decimal value;
            if (element != null)
                if (Decimal.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Decimal? DecimalValue(XElement element, Decimal? defaultValue)
        {
            Decimal value;
            if (element != null)
                if (Decimal.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Double DoubleValue(XElement element, Double defaultValue)
        {
            Double value;
            if (element != null)
                if (Double.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Double? DoubleValue(XElement element, Double? defaultValue)
        {
            Double value;
            if (element != null)
                if (Double.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Guid GuidValue(XElement element, Guid defaultValue)
        {
            if (element != null)
                try
                {
                    return new Guid(element.Value);
                }
                catch (Exception)
                {
                    return default(Guid);
                }
            return defaultValue;
        }
        protected static Guid? GuidValue(XElement element, Guid? defaultValue)
        {
            if (element != null)
                try
                {
                    return new Guid(element.Value);
                }
                catch (Exception)
                {
                    return default(Guid);
                }
            return defaultValue;
        }
        protected static Int16 Int16Value(XElement element, Int16 defaultValue)
        {
            Int16 value;
            if (element != null)
                if (Int16.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Int16? Int16Value(XElement element, Int16? defaultValue)
        {
            Int16 value;
            if (element != null)
                if (Int16.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Int64 Int64Value(XElement element, Int64 defaultValue)
        {
            Int64 value;
            if (element != null)
                if (Int64.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Int64? Int64Value(XElement element, Int64? defaultValue)
        {
            Int64 value;
            if (element != null)
                if (Int64.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static SByte SByteValue(XElement element, SByte defaultValue)
        {
            SByte value;
            if (element != null)
                if (SByte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static SByte? SByteValue(XElement element, SByte? defaultValue)
        {
            SByte value;
            if (element != null)
                if (SByte.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Single SingleValue(XElement element, Single defaultValue)
        {
            Single value;
            if (element != null)
                if (Single.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static Single? SingleValue(XElement element, Single? defaultValue)
        {
            Single value;
            if (element != null)
                if (Single.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static TimeSpan TimeSpanValue(XElement element, TimeSpan defaultValue)
        {
            if (element != null)
                try
                {
                    return XmlConvert.ToTimeSpan(element.Value);
                }
                catch (Exception)
                {
                    Globals.DefaultLogger(MajorEnum.Error, MinorEnum.SerializationError, "Given value is not a valid timespan (should conform to IS) 8601: {0}", new object[] { element.Value });
                }
            return defaultValue;
        }
        protected static TimeSpan? TimeSpanValue(XElement element, TimeSpan? defaultValue)
        {
            if (element != null)
                try
                {
                    return XmlConvert.ToTimeSpan(element.Value);
                }
                catch (Exception)
                {
                    Globals.DefaultLogger(MajorEnum.Error, MinorEnum.SerializationError, "Given value is not a valid timespan (should conform to IS) 8601: {0}", new object[] { element.Value });
                }
            return defaultValue;
        }
        protected static TimeZoneInfo TimeZoneInfoValue(XElement element, TimeZoneInfo defaultValue)
        {
            if (element != null)
                return TimeZoneInfo.FromSerializedString(element.Value);
            return defaultValue;
        }
        protected static UInt16 UInt16Value(XElement element, UInt16 defaultValue)
        {
            UInt16 value;
            if (element != null)
                if (UInt16.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt16? UInt16Value(XElement element, UInt16? defaultValue)
        {
            UInt16 value;
            if (element != null)
                if (UInt16.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt32 UInt32Value(XElement element, UInt32 defaultValue)
        {
            UInt32 value;
            if (element != null)
                if (UInt32.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt32? UInt32Value(XElement element, UInt32? defaultValue)
        {
            UInt32 value;
            if (element != null)
                if (UInt32.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt64 UInt64Value(XElement element, UInt64 defaultValue)
        {
            UInt64 value;
            if (element != null)
                if (UInt64.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        protected static UInt64? UInt64Value(XElement element, UInt64? defaultValue)
        {
            UInt64 value;
            if (element != null)
                if (UInt64.TryParse(element.Value, out value))
                    return value;
            return defaultValue;
        }
        // ReSharper disable PossibleNullReferenceException
        protected static Color ColorValue(XElement element, Color defaultValue)
        {
            ColorConverter cc = new ColorConverter();
            if (element != null && !string.IsNullOrEmpty(element.Value))
                return (Color)cc.ConvertFromString(element.Value);
            return defaultValue;
        }
        protected static Color? ColorValue(XElement element, Color? defaultValue)
        {
            ColorConverter cc = new ColorConverter();
            if (element != null)
                return (Color)cc.ConvertFromString(element.Value);
            return defaultValue;
        }
        // ReSharper restore PossibleNullReferenceException
        protected static Uri ColorValue(XElement element, Uri defaultValue)
        {
            if (element != null && !string.IsNullOrEmpty(element.Value))
                return new Uri(element.Value);
            return defaultValue;
        }

        // ReSharper restore UnusedMember.Global
        // ReSharper restore InconsistentNaming
        
        #endregion

        #region Other helper functions

        protected static List<string> ListStringValues(XElement element)
        {
            if (element == null)
                return null;
            List<string> values = new List<string>();
            foreach (XElement valueElement in element.Elements())
                values.Add(valueElement.Value);
            return values;
        }

        delegate void XmlWriteDelegate(string elementName, string value, string keyValue);

        // This should agree with SerializeSimpleValue in XmlSerializerBuilderType
        protected static void XmlValueFromObject(string typeName, object objectValue, string keyValue, XmlTextWriter xwrtr)
        {
            XmlWriteDelegate xwr = delegate(string tn, string ov, string kv)
            {
                xwrtr.WriteStartElement(tn);
                if (!string.IsNullOrEmpty(kv))
                    xwrtr.WriteAttributeString("key", kv);
                xwrtr.WriteString(ov);
                xwrtr.WriteEndElement();
            };
            Type propertyType = GetTypeForName(typeName);
            Type actualType = SerializerBuilderBaseType.GetActualType(propertyType);
            TypeContextType contextType = new TypeContextType { OriginalType = actualType };
            if (actualType == typeof(string))
            {
                xwr(contextType.TheTypeName, objectValue.ToString(), keyValue);
                return;
            }
            if (actualType == typeof(char))
            {

                xwr(typeName, XmlConvert.ToString((char)objectValue), keyValue);
                return;
            }
            if (actualType == typeof(Single))
            {
                xwr(typeName, DoubleConverter.ToExactString((Single)objectValue), keyValue);
                return;
            }
            if (actualType == typeof(Double))
            {
                xwr(typeName, DoubleConverter.ToExactString((Double)objectValue), keyValue);
                return;
            }
            if (actualType.IsEnum)
            {
                xwr(contextType.TheTypeName, objectValue.ToString(), keyValue);
                return;
            }
            if (actualType.Name == "Color")
            {
                xwr(contextType.TheTypeName, RgbForColor((Color)objectValue), keyValue);
                return;
            }
            if (actualType.Name == "Uri")
            {
                xwr(contextType.TheTypeName, objectValue.ToString(), keyValue);
                return;
            }
            if (actualType == typeof(DateTime))
            {
                xwr(contextType.TheTypeName, XmlConvert.ToString((DateTime)objectValue, Globals.DateTimeSerializationMode), keyValue);
                return;
            }
            if (actualType == typeof(TimeSpan))
            {
                xwr(contextType.TheTypeName, XmlConvert.ToString((TimeSpan)objectValue), keyValue);
                return;
            }
            xwr(contextType.TheTypeName, objectValue.ToString(), keyValue);
        }
        
        // This should agree with SerializeSimpleValue in JsonSerializerBuilderType
        protected static BsonValue BsonValueFromObject(string typeName, object objectValue)
        {
            Type propertyType = GetTypeForName(typeName);
            Type actualType = SerializerBuilderBaseType.GetActualType(propertyType);
            if (actualType == typeof(string))
                return BsonValue.Create(objectValue);
            if (actualType == typeof(char))
                return BsonValue.Create(string.Format("0x{0:X}", Convert.ToInt16(objectValue)));
            if (actualType == typeof(Single))
                return BsonValue.Create(DoubleConverter.ToExactString((Single)objectValue));
            if (actualType == typeof(Double))
                return BsonValue.Create(DoubleConverter.ToExactString((Double)objectValue));
            if (actualType.IsEnum)
                return BsonValue.Create(objectValue);
                //return string.Format("value.{0}.ToString()", property.Name + valueString);
            if (actualType.Name == "Color")
                return BsonValue.Create(RgbForColor((Color)objectValue));
            if (actualType.Name == "Uri")
                return BsonValue.Create(objectValue.ToString());
            if (actualType == typeof(DateTime))
                return BsonValue.Create(((DateTime)objectValue).ToString("s"));
            if (actualType == typeof(DateTimeOffset))
                return BsonValue.Create(((DateTimeOffset)objectValue).Ticks);
            if (actualType == typeof(TimeSpan))
                return BsonValue.Create(XmlConvert.ToString((TimeSpan)objectValue));
            if (actualType == typeof(decimal))
                return BsonValue.Create(objectValue.ToString());
            if (actualType.IsValueType)
                return BsonValue.Create(objectValue);

            return BsonValue.Create(objectValue.ToString());

        }

        protected static object ValueFromObject(Type t, object objectValue)
        {
            if (objectValue == null)
                return null;

            if (objectValue.GetType() == t)
                return objectValue;

            if (t == typeof(char))
            {
                return CharValue(objectValue.ToString(), default(char));
            }
            if (t == typeof(DateTimeOffset))
            {
                return DateTimeOffsetValue(objectValue.ToString(), default(DateTimeOffset));
            }
            if (t == typeof(Color))
            {
                return ColorValue(objectValue.ToString(), default(Color));
            }
            if (t == typeof(Uri))
            {
                return UriValue(objectValue, default(Uri));
            }
            if (t == typeof(Guid))
            {
                return GuidValue(objectValue.ToString(), default(Guid));
            }
            if (t == typeof(TimeSpan))
            {
                return TimeSpanValue(objectValue.ToString(), default(TimeSpan));
            }
            return Convert.ChangeType(objectValue.ToString(), t);
        }

        protected static object ValueFromString(string valueTypeName, string stringValue)
        {
            if (string.IsNullOrEmpty(stringValue))
                return null;

            Type t = GetTypeForName(valueTypeName);


            if (t == typeof(char))
            {
                return CharValue(stringValue, default(char));
            }
            if (t == typeof(DateTimeOffset))
            {
                return DateTimeOffsetValue(stringValue, default(DateTimeOffset));
            }
            if (t == typeof(Color))
            {
                return ColorValue(stringValue, default(Color));
            }
            if (t == typeof(Uri))
            {
                return UriValue(stringValue, default(Uri));
            }
            if (t == typeof(Guid))
            {
                return GuidValue(stringValue, default(Guid));
            }
            if (t == typeof(TimeSpan))
            {
                return TimeSpanValue(stringValue, default(TimeSpan));
            }
            return System.Convert.ChangeType(stringValue, t);
        }

        public delegate object DeSerializeFromXElementDelegate(string typeName, XElement element);

        protected static object ValueFromXElement(string valueTypeName, XElement propertyElement, DeSerializeFromXElementDelegate deSerializeFromXElement)
        {
            if (string.IsNullOrEmpty(propertyElement.Value))
                return null;
            if (propertyElement.Attribute("Count") != null)
            {
                object value = null;
                foreach (XElement element in propertyElement.Elements())
                {
                    if (value == null)
                    {
                        if (element.Attribute("key") != null)
                        {
                            Type valueType = typeof(Dictionary<,>).MakeGenericType(typeof(object), typeof(object));
                            value = valueType.GetConstructor(new Type[] { }).Invoke(new object[] { });
                        }
                        else
                        {
                            Type valueType = typeof(List<>).MakeGenericType(GetTypeForName(element.Name.LocalName));
                            value = valueType.GetConstructor(new Type[] { }).Invoke(new object[] { });
                        }
                    }
                    if (value is IList)
                        ((IList)value).Add(deSerializeFromXElement(element.Name.LocalName, element));
                    else if (element.Attribute("key") != null)
// ReSharper disable PossibleNullReferenceException
                        ((IDictionary)value).Add(element.Attribute("key").Value, deSerializeFromXElement(element.Name.LocalName, element));
// ReSharper restore PossibleNullReferenceException
                }
                return value;
            }
            
            XElement valueElement = propertyElement.FirstElement();
            if (valueElement == null)
                return null;
            Type targetType = GetTypeForName(valueElement.Name.LocalName);
            if (targetType != typeof(object))
            {
                if (targetType == typeof(TimeSpan))
                    return XmlConvert.ToTimeSpan(valueElement.Value);
                if (targetType == typeof(Color))
                    return ColorValue(valueElement.Value, default(Color));
                if (targetType == typeof(Uri))
                    return UriValue(valueElement.Value, default(Uri));
                if (targetType == typeof(char))
                    return XmlConvert.ToChar(valueElement.Value);
                return Convert.ChangeType(valueElement.Value, targetType);
            }
            return valueElement;
        }

        public delegate object DeSerializeFromJsonDelegate(string typeName, BsonValue doc);

        protected static object ValueFromJson(BsonValue bsonValue, DeSerializeFromJsonDelegate deSerializeFromJson)
        {
            if (bsonValue == null || (bsonValue.RawValue == null && !bsonValue.IsBsonArray && !bsonValue.IsBsonDocument))
                return null;

            if (bsonValue.IsBsonArray)
            {
                BsonArray arr = bsonValue.AsBsonArray;
                if (arr[0].IsString && arr[0].AsString == "Dictionary!")
                {
                    Type valueType = typeof(Dictionary<,>).MakeGenericType(typeof(object), typeof(object));
                    IDictionary value = (IDictionary)valueType.GetConstructor(new Type[] { }).Invoke(new object[] { });
                    foreach(BsonElement element in arr[1].ToBsonDocument())
                    {
                        value.Add(element.Name, deSerializeFromJson(element.Value.RawValue.GetType().Name, element.Value)); // wrong! - sort it out in the debugger
                    }
                    return value;
                }
                else
                {
                    IList value = null;
                    Type valueType = null;
                    foreach (BsonValue val in arr)
                    {
                            if (val is BsonDocument)
                            {
                                object v = deSerializeFromJson(((BsonDocument) val).First().Name,
                                                               ((BsonDocument) val).First().Value);
                                if (value == null)
                                {
                                    valueType = v.GetType();
                                    Type listType = typeof (List<>).MakeGenericType(valueType);
                                    value = (IList) listType.GetConstructor(new Type[] {}).Invoke(new object[] {});
                                }
                                value.Add(v);
                            }
                            else
                            {
                                if (value == null)
                                {
                                    valueType = val.RawValue.GetType();
                                    Type listType = typeof (List<>).MakeGenericType(valueType);
                                    value = (IList) listType.GetConstructor(new Type[] {}).Invoke(new object[] {});
                                }
                                value.Add(deSerializeFromJson(valueType.Name, val));
                            }
                    }
                    return value;
                }
            }

            if (bsonValue.IsBsonDocument)
            {
                object value = null;
                foreach (BsonElement element in bsonValue.AsBsonDocument)
                {
                    if (value != null)
                        throw new ApplicationException("Not expecting a dictionary here...");
                    value = deSerializeFromJson(element.Name, element.Value);
                }
                return value;
            }

            if (bsonValue.RawValue == null)
                return null;
            
            Type targetType = bsonValue.RawValue.GetType();  
            // this won't work - need to figure out how to get the type name
            // and then use GetTypeForName to get the actual type

            return deSerializeFromJson(targetType.Name, bsonValue);
        }

        public static Type GetTypeForName(string typeName)
        {
            switch (typeName.ToLower())
            {
                case "int": return typeof(int);
                case "string": return typeof(string);
                case "int32": return typeof(Int32);
                case "bool": return typeof(bool);
                case "boolean": return typeof(Boolean);
                case "float": return typeof(float);
                case "uint": return typeof(uint);
                case "long": return typeof(long);
                case "ulong": return typeof(ulong);
                case "short": return typeof(short);
                case "ushort": return typeof(ushort);
                case "byte": return typeof(Byte);
                case "char": return typeof(Char);
                case "datetime": return typeof(DateTime);
                case "datetimeoffset": return typeof(DateTimeOffset);
                case "decimal": return typeof(Decimal);
                case "double": return typeof(Double);
                case "guid": return typeof(Guid);
                case "int16": return typeof(Int16);
                case "int64": return typeof(Int64);
                case "sbyte": return typeof(SByte);
                case "single": return typeof(Single);
                case "timespan": return typeof(TimeSpan);
                case "timezoneinfo": return typeof(TimeZoneInfo);
                case "uint16": return typeof(UInt16);
                case "uint32": return typeof(UInt32);
                case "uint64": return typeof(UInt64);
                case "color": return typeof(Color);
                case "uri": return typeof(Uri);
                default: return typeof(object);
            }
        }

        protected static TAType[] ArrayValues<TAType>(XElement element)
        {
            if (element == null)
                return null;
            TAType[] values = new TAType[element.Elements().Count()];
            int index = 0;
            foreach (XElement valueElement in element.Elements())
            {
                values[index] = (TAType)System.Convert.ChangeType(valueElement.Value, typeof(TAType));
                index++;
            }
            return values;
        }

        protected static List<TAType> ListValues<TAType>(XElement element)
        {
            if (element == null)
                return null;
            List<TAType> values = new List<TAType>();
            if (typeof(TAType) == typeof(char))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType) (object) CharValue(valueElement.Value, default(char)));
            }
            else if (typeof(TAType) == typeof(DateTimeOffset))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)DateTimeOffsetValue(valueElement.Value, default(DateTimeOffset)));
            }
            else if (typeof(TAType) == typeof(Color))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)ColorValue(valueElement.Value, default(Color)));
            }
            else if (typeof(TAType) == typeof(Uri))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)UriValue(valueElement.Value, default(Uri)));
            }
            else if (typeof(TAType) == typeof(Guid))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)GuidValue(valueElement.Value, default(Guid)));
            }
            else if (typeof(TAType) == typeof(TimeSpan))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)TimeSpanValue(valueElement.Value, default(TimeSpan)));
            }
            else if (typeof(TAType) == typeof(DateTime))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)DateTimeValue(valueElement.Value, default(DateTime)));
            }
            else
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)System.Convert.ChangeType(valueElement.Value, typeof(TAType)));
            }
            return values;
        }

        protected static HashSet<TAType> HashSetValues<TAType>(XElement element)
        {
            if (element == null)
                return null;
            HashSet<TAType> values = new HashSet<TAType>();
            if (typeof(TAType) == typeof(char))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)CharValue(valueElement.Value, default(char)));
            }
            else if (typeof(TAType) == typeof(DateTimeOffset))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)DateTimeOffsetValue(valueElement.Value, default(DateTimeOffset)));
            }
            else if (typeof(TAType) == typeof(Color))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)ColorValue(valueElement.Value, default(Color)));
            }
            else if (typeof(TAType) == typeof(Uri))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)UriValue(valueElement.Value, default(Uri)));
            }
            else if (typeof(TAType) == typeof(Guid))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)GuidValue(valueElement.Value, default(Guid)));
            }
            else if (typeof(TAType) == typeof(TimeSpan))
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)(object)TimeSpanValue(valueElement.Value, default(TimeSpan)));
            }
            else
            {
                foreach (XElement valueElement in element.Elements())
                    values.Add((TAType)System.Convert.ChangeType(valueElement.Value, typeof(TAType)));
            }
            return values;
        }

        protected static List<TAType> ListValues<TAType>(object values)
        {
            if (values == null)
                return null;
            List<TAType> newValues = new List<TAType>();
            if (typeof(TAType).IsEnum)
                foreach (BsonValue value in (BsonArray)values)
                    newValues.Add(GetEnumValue<TAType>(value.ToInt32()));
            else
                foreach (BsonValue value in (BsonArray)values)
                    newValues.Add((TAType)ValueFromObject(typeof(TAType), value));
            return newValues;
        }

        protected static TAType GetEnumValue<TAType>(object value)
        {
            return (TAType)Enum.ToObject(typeof (TAType), value);
        }

        protected static HashSet<TAType> HashSetValues<TAType>(object values)
        {
            if (values == null)
                return null;
            HashSet<TAType> hs = new HashSet<TAType>();
            if (typeof(TAType).IsEnum)
                foreach (BsonValue value in (BsonArray)values)
                    hs.Add(GetEnumValue<TAType>(value.ToInt32()));
            else
                foreach (BsonValue value in (BsonArray)values)
                    hs.Add((TAType)Convert.ChangeType(value, typeof(TAType)));
            return hs;
        }

        protected static IDictionary GetObjectDictionary(
            XElement element, Type aType, GetDictionaryDelegate getDictionary, GetKeyDelegate getKey, System.Collections.IDictionary defaultValue)
        {
            if (element == null)
                return defaultValue;
            IDictionary dictionary = getDictionary();

            // ReSharper disable PossibleNullReferenceException
            // if any of the valueElement.Attribute("Key") expressions are null, the underlying object is 
            // corrupted and a nullreference is probably as good an execption as any to throw at this point.
            if (aType == typeof(char))
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), CharValue(valueElement.Value, default(char)));
            }
            else if (aType == typeof(DateTimeOffset))
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), DateTimeOffsetValue(valueElement.Value, default(DateTimeOffset)));
            }
            else if (aType == typeof(Color))
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), ColorValue(valueElement.Value, default(Color)));
            }
            else if (aType == typeof(Uri))
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), UriValue(valueElement.Value, default(Uri)));
            }
            else if (aType == typeof(Guid))
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), GuidValue(valueElement.Value, default(Guid)));
            }
            else if (aType == typeof(TimeSpan))
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), TimeSpanValue(valueElement.Value, default(TimeSpan)));
            }
            else if (aType == typeof(DateTime))
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), DateTimeValue(valueElement.Value, default(DateTime)));
            }
            else
            {
                foreach (XElement valueElement in element.Elements())
                    dictionary.Add(getKey(valueElement.Attribute("Key").Value), Convert.ChangeType(valueElement.Value, aType));
            }
            // ReSharper restore PossibleNullReferenceException
            return dictionary;
        }

        protected static IDictionary GetObjectDictionary(
            object objectDoc, Type aType, GetDictionaryDelegate getDictionary, GetKeyDelegate getKey, IDictionary defaultValue)
        {
            if (objectDoc == null)
                return defaultValue;
            BsonDocument doc = (BsonDocument)objectDoc;
            IDictionary dictionary = getDictionary();
            foreach (string name in doc.Names)
                dictionary.Add(getKey(name), ValueFromObject(aType, doc[name]));
            return dictionary;
        }

        protected static object ReferenceValue(XElement element, Type referenceType)
        {
            throw new NotImplementedException();
            //return CIP4.Simple.CIP4XmlSerializerType.GetReferencePropertyValue(new CIP4.Item.CompoundValueType(element), referenceType);

        }

        protected static object ReferenceValue(object value, Type referenceType)
        {
            throw new NotImplementedException();
            //object result = Activator.CreateInstance(referenceType, new object[] { value });
            //return CIP4.Simple.CIP4XmlSerializerType.GetReferencePropertyValue(new CIP4.Item.CompoundValueType(element), referenceType);

        }

        protected static string GetExternalIdFromReferenceElement(XElement element)
        {
            if (element == default(XElement))
                return string.Empty;

            XElement idElement = element.Descendants(XName.Get("TargetExternalID", Globals.cioNS)).FirstOrDefault();
            if (idElement != default(XElement))
                return idElement.Value;
            return string.Empty;
        }

        protected static string GetExternalIdFromReferenceDoc(BsonDocument doc)
        {
            if (doc != null && doc.Contains("TargetExternalID"))
                return doc["TargetExternalID"].ToString();
            return string.Empty;
        }

        protected static object GetEnum(XElement element, Type enumType, object defaultValue)
        {
            if (element == null || !Enum.IsDefined(enumType, element.Value))
                return defaultValue;
            return Enum.Parse(enumType, element.Value, true);
        }

        public static object GetEnum(string value, Type enumType, object defaultValue)
        {
            if (string.IsNullOrEmpty(value) || !Enum.IsDefined(enumType, value))
                return defaultValue;
            return Enum.Parse(enumType, value, true);
        }

        protected static object GetValueFromDoc(string valueName, BsonDocument doc)
        {
            if (doc.Contains(valueName))
                return doc[valueName];
            return null;
        }

        protected static string GetStringValueFromDoc(string valueName, BsonDocument doc)
        {
            if (doc.Contains(valueName))
                return doc[valueName].ToString();
            return null;
        }

        /// <summary>
        /// See comments on ExternalIDType.ExternalIDEscape
        /// </summary>
        /// <param name="raw"></param>
        /// <returns></returns>
        public static string ExternalIDEscape(string raw)
        {
            return ExternalIDType.ExternalIDEscape(raw);
        }

        /// <summary>
        /// See comments on ExternalIDType.ExternalIDUnescape
        /// </summary>
        /// <param name="encoded"></param>
        /// <returns></returns>
        public static string ExternalIDUnescape(string encoded)
        {
            return ExternalIDType.ExternalIDUnescape(encoded);
        }

        protected delegate ExternalIDType GetExternalIDDelegate(XElement element);

        protected static string GetExternalIDFromElements(string propertyName, XElement propertyElement, XElement itemElement, GetExternalIDDelegate getId)
        {
            XElement valueElement = propertyElement == null ? null : propertyElement.Element(XName.Get(propertyName, Globals.cioNS));
            string result = valueElement == null ? null : valueElement.Value;
            if (string.IsNullOrEmpty(result))
            {
                ExternalIDType externalID = getId(itemElement);
                if (externalID != null)
                    return ExternalIDType.ExternalIDUnescape(externalID.ToString());
            }
            return result;
        }

        public static byte[] Compress(string dataIn)
        {
            byte[] data = Encoding.Unicode.GetBytes(dataIn);
            MemoryStream ms = new MemoryStream();
            GZipStream gz = new GZipStream(ms, CompressionMode.Compress);
            gz.Write(data, 0, data.Length);
            gz.Close();
            return ms.ToArray();
        }

        public static string DeCompress(byte[] data)
        {
            GZipStream gz = new GZipStream(new MemoryStream(data), CompressionMode.Decompress);
            StreamReader rdr = new StreamReader(gz, Encoding.Unicode);
            return rdr.ReadToEnd();
        }

        protected static XElement GetXmlData(object fromObject)
        {
            if (fromObject == null)
                return null;
            if (fromObject is XElement)
                return (XElement)fromObject;
            if (fromObject is string)
                return XElement.Parse((string)fromObject);
            throw new ArgumentException(string.Format("Don't know how to transform an object of type {0} to XElement", fromObject.GetType().Name));
        }

        protected static BsonDocument GetDocumentData(object fromObject, Type aType)
        {
            if (fromObject == null)
                return null;
            
            if (fromObject is BsonDocument)
                return (BsonDocument)fromObject;
            
            if (fromObject is string)
                return BsonSerializer.Deserialize<BsonDocument>(new StringReader((string)fromObject));

            throw new ArgumentException(string.Format("Don't know how to transform an object of type {0} to MongoDB.Document", fromObject.GetType().Name));
        }

        public delegate void SerializeToXmlDelegate(string typeName, object value, string key, XmlTextWriter xwrtr);
        
        protected static void SerializeVersions(string versionsPropertyName, CIP4ItemVersionsType versions, XmlTextWriter xwrtr, SerializeToXmlDelegate serializeToXml)
        {
            if (versions.Versions.Count == 0)
                return;
            xwrtr.WriteStartElement(versionsPropertyName);
            foreach (KeyValuePair<long, CIP4ItemVersionType> kvp in versions.Versions)
            {
                xwrtr.WriteStartElement("Version");
                xwrtr.WriteAttributeString("_version", kvp.Key.ToString());
                SerializeVersion(kvp.Value, xwrtr, serializeToXml);
                xwrtr.WriteEndElement();
            }
            xwrtr.WriteEndElement();
        }

        private static void SerializeVersion(CIP4ItemVersionType version, XmlTextWriter xwrtr, SerializeToXmlDelegate serializeToXml)
        {
            foreach (KeyValuePair<string, object> kvp in version.Values)
            {
                if (kvp.Value is IList)
                {
                    xwrtr.WriteStartElement(kvp.Key);
                    xwrtr.WriteAttributeString("Count", ((IList)kvp.Value).Count.ToString());
                    foreach (object value in (IList)kvp.Value)
                    {
                        if (value is XElement)
                        {
                            xwrtr.WriteRaw(value.ToString());
                        }
                        else
                        {
                            string elementName = SerializerBuilderBaseType.GetActualType(value.GetType()).Name;
                            serializeToXml(elementName, value, string.Empty, xwrtr);
                        }
                    }
                    xwrtr.WriteEndElement();
                }
                else if (kvp.Value is IDictionary)
                {
                    xwrtr.WriteStartElement(kvp.Key);
                    xwrtr.WriteAttributeString("Count", ((IDictionary)kvp.Value).Count.ToString());
                    foreach (DictionaryEntry value in (IDictionary)kvp.Value)
                    {
                        string elementName = SerializerBuilderBaseType.GetActualType(value.Value.GetType()).Name;
                        serializeToXml(elementName, value.Value, value.Key.ToString(), xwrtr);
                    }
                    xwrtr.WriteEndElement();
                }
                else
                {
                    xwrtr.WriteStartElement(kvp.Key);
                    if (kvp.Value is XElement)
                    {
                        xwrtr.WriteRaw(kvp.Value.ToString());
                    }
                    else
                    {
                        string elementName = SerializerBuilderBaseType.GetActualType(kvp.Value.GetType()).Name;
                        serializeToXml(elementName, kvp.Value, string.Empty, xwrtr);
                    }
                    xwrtr.WriteEndElement();
                }
            }
        }

        public static List<T> MvVersion<T>(string propertyName, Dictionary<string, object> values, IList<T> previousValues)
        {
            List<T> newValues = previousValues == null ? new List<T>() : new List<T>(previousValues);

            if (values.ContainsKey(propertyName + "_deleteThese"))
            {
                IList deletedIndexes = (IList)values[propertyName + "_deleteThese"];
                for (int index = deletedIndexes.Count - 1; index >= 0; index--)
                {
                    newValues.RemoveAt((int)deletedIndexes[index]);
                }
            }
            if (values.ContainsKey(propertyName))
            {
                IList addedIndexes = (IList)values[propertyName + "_added"];
                IList added = (IList)values[propertyName];
                for (int index = addedIndexes.Count - 1; index >= 0; index--)
                {
                    newValues.Insert(
                        Math.Min((int)addedIndexes[index], newValues.Count),
                        (T)ValueFromObject(typeof(T), added[index]));
                }
            }

            return newValues.Count == 0 ? null : newValues;
        }

        public static Dictionary<TK, TT> MvVersion<TK, TT>(string propertyName, Dictionary<string, object> values, IDictionary<TK, TT> previousValues)
        {
            Dictionary<TK, TT> newValues = previousValues == null ? new Dictionary<TK, TT>() : new Dictionary<TK, TT>(previousValues);

            if (values.ContainsKey(propertyName + "_deleteThese"))
            {
                IList deletedKeys = (IList)values[propertyName + "_deleteThese"];
                foreach (object key in deletedKeys)
                {
                    newValues.Remove((TK)ValueFromObject(typeof(TK), key));
                }
            }
            if (values.ContainsKey(propertyName))
            {
                foreach (DictionaryEntry de in (IDictionary)values[propertyName])
                {
                    newValues.Add(
                        (TK)ValueFromObject(typeof(TK), de.Key),
                        (TT)ValueFromObject(typeof(TT), de.Value));
                }
            }

            return newValues.Count == 0 ? null : newValues;
        }

        // put these here rather than in CIP4ItemVersionsType because don't want to have to reference the mongo stuff in that assembly

        public delegate BsonValue SerializeToJsonDelegate(string typeName, object value);

        public static BsonDocument JsonSerializeVersion(CIP4ItemVersionsType version, MongoDB.Bson.BsonDocument doc, SerializeToJsonDelegate serializeToJson)
        {
            BsonArray versions = new BsonArray();

            foreach (KeyValuePair<long, CIP4ItemVersionType> kvp in version.Versions)
            {
                versions.Add(JsonSerializeVersion(kvp.Value, kvp.Key, serializeToJson));
            }

            return doc.Add("Versions", versions);
        }

        public static BsonDocument JsonSerializeVersion(CIP4ItemVersionType itemVersion, long version, SerializeToJsonDelegate serializeToJson)
        {
            BsonDocument doc = new BsonDocument();
            foreach (KeyValuePair<string, object> kvp in itemVersion.Values)
            {
                if (kvp.Value is IList)
                {
                    BsonArray values = new BsonArray();
                    foreach (object val in (IList)kvp.Value)
                    {
                        values.Add(serializeToJson(val.GetType().Name, val));
                    }
                    doc.Add(kvp.Key, values);
                }
                else if (kvp.Value is IDictionary)
                {
                    // need this so we can distinguishe between a dictionary and a compound type
                    BsonArray arrayWrapper = new BsonArray();
                    BsonDocument values = new BsonDocument();
                    foreach (DictionaryEntry de in (IDictionary)kvp.Value)
                        values.Add(de.Key.ToString(), serializeToJson(de.Value.GetType().Name, de.Value));
                    arrayWrapper.Add("Dictionary!");
                    arrayWrapper.Add(values);
                    doc.Add(kvp.Key, arrayWrapper);
                }
                else
                {
                    if (kvp.Value == null)
                        doc.Add(kvp.Key, new BsonDocument());
                    else
                        doc.Add(kvp.Key, serializeToJson(kvp.Value.GetType().Name, kvp.Value));
                }
            }
            doc.Add("_version", version);
            return doc;
        }

        public class EqualityComparer<T> : IEqualityComparer<T>
        {
            private readonly Func<T, T, bool> _equalsFn;
            private readonly Func<T, int> _getHashCodefn;

            public EqualityComparer(Func<T, T, bool> equalsFn, Func<T, int> getHashCodefn)
            {
                _equalsFn = equalsFn;
                _getHashCodefn = getHashCodefn;
            }

            public bool Equals(T x, T y)
            {
                return _equalsFn(x, y);
            }

            public int GetHashCode(T obj)
            {
                return _getHashCodefn(obj);
            }
        }

        #endregion
    }

}
